package com.my.magicApi.scripts;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.my.magicApi.annotation.MagicAuthority;
import com.my.magicApi.annotation.enums.AuthorityTypeEnum;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.security.core.context.SecurityContextHolder;
import org.ssssssss.magicapi.core.config.MagicFunction;
import org.ssssssss.script.annotation.Comment;
import org.ssssssss.script.annotation.Function;
import org.ssssssss.script.functions.DateExtension;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 自定义函数
 * https://ssssssss.org/magic-api/pages/senior/function/
 */
public class CustomFunction implements MagicFunction {

	// 脚本中直接使用 now();
	@Function
	@Comment("取当前时间")
	public Date now() {
		return new Date();
	}

	// 脚本中使用 date_format(now())
	@Function
	@Comment("日期格式化")
	public String date_format(@Comment(name = "target", value = "目标日期") Date target) {
		return target == null ? null : DateExtension.format(target, "yyyy-MM-dd HH:mm:ss");
	}

	// 脚本中使用 date_format(now(),'yyyy-MM-dd')
	@Function
	@Comment("日期格式化")
	public String date_format(@Comment(name = "target", value = "目标日期") Date target, @Comment(name = "pattern", value = "格式") String pattern) {
		return target == null ? null : DateExtension.format(target, pattern);
	}

	// 脚本中直接使用ifnull() 调用
	@Function
	@Comment("判断值是否为空")
	public Object ifnull(@Comment(name = "target", value = "目标值") Object target, @Comment(name = "trueValue", value = "为空的值") Object trueValue, @Comment(name = "falseValue", value = "不为空的值") Object falseValue) {
		return target == null ? trueValue : falseValue;
	}
	@Function
	@Comment("获取用户信息")
	@MagicAuthority(authorityType = AuthorityTypeEnum.ADD)
	public static Object getUserInfo() throws Exception {
		return SecurityContextHolder.getContext().getAuthentication();
	}


	@Function
	@Comment("组装mongodb查询条件")
	public static Criteria criteriaBuilder(@Comment("条件参数")Object query) throws Exception {
		JSONObject queryObject = (JSONObject) JSONObject.toJSON(query);
		Criteria whereCriteria = new Criteria();
		Field criteriaChain = whereCriteria.getClass().getDeclaredField("criteriaChain");
		criteriaChain.setAccessible(true);
		List<Criteria> criteria = (List<Criteria>)criteriaChain.get(whereCriteria);
		if (ObjectUtils.isNotEmpty(queryObject)){
			whereCriteriaBuilder(criteria,queryObject.getJSONObject("match"));
			inCriteriaBuilder(criteria,queryObject.getJSONObject("in"));
			existCriteriaBuilder(criteria,queryObject.getJSONObject("exist"));
			dateCriteriaBuilder(criteria,queryObject.getJSONObject("date"));
			eventCriteriaBuilder(criteria,queryObject.getJSONObject("event"));
			likeCriteriaBuilder(criteria,queryObject.getJSONObject("like"));
		}
		return whereCriteria;
	}
	private static void whereCriteriaBuilder(List<Criteria> criteria, JSONObject where) {
		if (ObjectUtils.isNotEmpty(where)){
			try {
				List<Criteria> criteriaList = new ArrayList<>();

				for (Map.Entry<String, Object> set : where.entrySet()) {
					criteriaList.add(Criteria.where(set.getKey()).is(set.getValue()));
				}
				criteria.addAll(criteriaList);
			}catch (Exception e){
				e.printStackTrace();
			}
		}
	}
	private static void inCriteriaBuilder(List<Criteria> criteria, JSONObject in) {
		if (ObjectUtils.isNotEmpty(in)){
			try {
				List<Criteria> criteriaList = new ArrayList<>();

				for (Map.Entry<String, Object> set : in.entrySet()) {
					criteriaList.add(Criteria.where(set.getKey()).in(in.getJSONArray(set.getKey())));
				}
				criteria.addAll(criteriaList);
			}catch (Exception e){
				e.printStackTrace();
			}
		}
	}
	private static void existCriteriaBuilder(List<Criteria> criteria, JSONObject exist) {
		if (ObjectUtils.isNotEmpty(exist)){
			try {
				List<Criteria> criteriaList = new ArrayList<>();

				for (Map.Entry<String, Object> set : exist.entrySet()) {
					criteriaList.add(Criteria.where(set.getKey()).exists(exist.getBooleanValue(set.getKey())));
				}
				criteria.addAll(criteriaList);
			}catch (Exception e){
				e.printStackTrace();
			}
		}
	}
	private static void dateCriteriaBuilder(List<Criteria> criteria, JSONObject date) {
		if (ObjectUtils.isNotEmpty(date)){
			try {
				List<Criteria> criteriaList = new ArrayList<>();
				for (Map.Entry<String, Object> set : date.entrySet()) {
					JSONArray dateArray = date.getJSONArray(set.getKey());
					if (CollectionUtils.isNotEmpty(dateArray)){
						Date startDate = DateUtil.parse(dateArray.getString(0));
						Date endDate = DateUtil.parse(dateArray.getString(1));
						criteriaList.add(Criteria.where(set.getKey()).gte(startDate)
								.lte(endDate));
					}
				}
				criteria.addAll(criteriaList);
			}catch (Exception e){
				e.printStackTrace();
			}
		}
	}
	private static void eventCriteriaBuilder(List<Criteria> criteria, JSONObject event) {
		if (ObjectUtils.isNotEmpty(event)){
			String eventName = event.getString("eventName");
			try {
				Criteria eventCriteria = new Criteria();
				Field criteriaChain = eventCriteria.getClass().getDeclaredField("criteriaChain");
				criteriaChain.setAccessible(true);
				List<Criteria> criteriaList = (List<Criteria>)criteriaChain.get(eventCriteria);
				JSONObject condition = event.getJSONObject("condition");
				for (Map.Entry<String, Object> set : condition.entrySet()) {
					criteriaList.add(Criteria.where(set.getKey()).is(condition.getIntValue(set.getKey())));
				}

				criteria.add(Criteria.where(eventName).elemMatch(eventCriteria));
			}catch (Exception e){
				e.printStackTrace();
			}
		}
	}
	private static void likeCriteriaBuilder(List<Criteria> criteria, JSONObject like) {
		if (ObjectUtils.isNotEmpty(like)){
			try {
				List<Criteria> criteriaList = new ArrayList<>();

				for (Map.Entry<String, Object> set : like.entrySet()) {
					JSONArray jsonArray = like.getJSONArray(set.getKey());
					List<Pattern> regexList = jsonArray.stream().map(item -> Pattern.compile("^.*" + item +".*$", Pattern.CASE_INSENSITIVE)).collect(Collectors.toList());
					criteriaList.add(Criteria.where(set.getKey()).in(regexList));
				}
				criteria.addAll(criteriaList);

			}catch (Exception e){
				e.printStackTrace();
			}
		}
	}
}
